Spring WebClient 教程

您所在的位置:网站首页 spring batch没啥用 Spring WebClient 教程

Spring WebClient 教程

2024-07-15 11:50| 来源: 网络整理| 查看: 265

1、概览

本文将带你了解如何使用 WebClient 和 WebTestClient,前者是一个 Spring 5 中引入的响应式 HTTP 客户端,而后者是一种用于测试的 WebClient。

2、WebClient 是啥?

简而言之,WebClient 是一个接口,表示执行 Web 请求的主要入口点。

它是 Spring Web Reactive 模块的一部分,用于取代经典的 RestTemplate。此外,这个新的客户端是一个基于 HTTP/1.1 协议的响应式、非阻塞解决方案。

尽管它实际上是一个非阻塞客户端,而且属于 spring-webflux 库,但该解决方案同时支持 同步 和 异步 操作,因此也适用于在 Servlet 技术栈上运行的应用,通过阻塞操作来获得结果。当然,如果使用的是响应式技术栈,则不建议采用这种做法。

该接口只有一个实现,即我们将要使用的 DefaultWebClient 类。

3、依赖

在 Spring Boot 应用中,只需要添加 spring-boot-starter-webflux 依赖即可获得响应式 Web 的支持。

3.1、使用 Maven 构建

在 pom.xml 中添加以下依赖:

org.springframework.boot spring-boot-starter-webflux 3.2、使用 Gradle 构建

build.gradle 文件中添加以下条目:

dependencies { compile 'org.springframework.boot:spring-boot-starter-webflux' } 4、使用 WebClient

要使用 WebClient,我们需要了解如何:

创建实例 发起请求 处理响应 4.1、创建 WebClient 实例

有三种方式可供选择。第一种是使用默认设置创建 WebClient 对象:

WebClient client = WebClient.create();

第二种方式是使用给定的基本 URI 初始化 WebClient 实例:

WebClient client = WebClient.create("http://localhost:8080");

第三种方式(推荐)是使用 DefaultWebClientBuilder 类构建客户端,该类允许完全自定义:

WebClient client = WebClient.builder() .baseUrl("http://localhost:8080") .defaultCookie("cookieKey", "cookieValue") .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .defaultUriVariables(Collections.singletonMap("url", "http://localhost:8080")) .build(); 4.2、指定超时时间

通常情况下,默认的 30 秒 HTTP 超时时间太慢,无法满足需要,要自定义这种行为,可以创建一个 HttpClient 实例,并配置 WebClient 使用它。

通过 ChannelOption.CONNECT_TIMEOUT_MILLIS 选项设置连接超时。 分别使用 ReadTimeoutHandler 和写 WriteTimeoutHandler 设置读、写超时。 使用 responseTimeout 指令配置响应超时。

所有这些都必须在要配置的 HttpClient 实例中指定:

HttpClient httpClient = HttpClient.create() .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) .responseTimeout(Duration.ofMillis(5000)) .doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS)) .addHandlerLast(new WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS))); WebClient client = WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) .build();

注意,虽然也可以在客户端请求中调用 timeout,但这是信号超时,而不是 HTTP 连接、读/写或响应超时;这是 Mono / Flux Publisher 的超时。

4.3、准备请求 - 定义方法

首先,需要通过调用 method(HttpMethod method) 来指定请求的 HTTP 方法:

UriSpec uriSpec = clienthod(HttpMethod.POST);

或调用其快捷方法,如 get、post 和 delete:

UriSpec uriSpec = client.post();

注意:虽然看起来重用了 Request Spec 变量(WebClient.UriSpec、WebClient.RequestBodySpec、WebClient.RequestHeadersSpec、WebClient.ResponseSpec),但这只是为了简化演示不同的方法。这些指令不应该在不同请求中重复使用,因为它们会检索引用,因此后面的操作会修改在前面步骤中所定义的内容。

4.4、准备请求 - 定义 URL

下一步是提供 URL。同样,也有不同的方法。

可以将其作为字符串传递给 uri API:

RequestBodySpec bodySpec = uriSpec.uri("/resource");

使用 UriBuilder Function 接口:

RequestBodySpec bodySpec = uriSpec.uri( uriBuilder -> uriBuilder.pathSegment("/resource").build());

或者,传递一个 java.net.URL 实例:

RequestBodySpec bodySpec = uriSpec.uri(URI.create("/resource"));

注意,如果为 WebClient 定义了默认的基本 URL,那么最后一个方法将覆盖该值。

4.5、准备请求 - 定义请求体

然后,可以根据需要设置请求体(Body)、Content Type、Length、Cookie 或 Header。

例如,如果要设置请体,有几种可用的方法。最常见、最直接的方法可能就是使用 bodyValue 方法:

RequestHeadersSpec headersSpec = bodySpec.bodyValue("data");

或者通过将一个 Publisher(以及将要发布的元素类型)传递给 body 方法来实现:

RequestHeadersSpec headersSpec = bodySpec.body( Mono.just(new Foo("name")), Foo.class);

另外,还可以使用 BodyInserters 工具类。例如,来看看如何像使用 bodyValue 方法那样,使用一个简单的对象来作为请求体。

RequestHeadersSpec headersSpec = bodySpec.body( BodyInserters.fromValue("data"));

同样,如果使用的是 Reactor 实例,也可以使用 BodyInserters#fromPublisher 方法:

RequestHeadersSpec headersSpec = bodySpec.body( BodyInserters.fromPublisher(Mono.just("data")), String.class);

该类还提供其他直观的功能,以覆盖更高级的应用场景。例如,发送 multipart 请求:

LinkedMultiValueMap map = new LinkedMultiValueMap(); map.add("key1", "value1"); map.add("key2", "value2"); RequestHeadersSpec headersSpec = bodySpec.body( BodyInserters.fromMultipartData(map));

所有这些方法都会创建一个 BodyInserter 实例,然后可以将其作为请求体(Request Body)。

BodyInserter 是一个接口,负责用给定的输出消息和插入时使用的 Context 填充 ReactiveHttpOutputMessage。

Publisher 是一个响应式组件,负责提供数量可能无限的序列化元素。它也是一个接口,最常用的实现是 Mono 和 Flux。

4.6、准备请求 - 定义 Header

设置 body 后,可以设置 Header、Cookie 和可接受的媒体类型。这些值将添加到实例化客户端时已设置的值中。

此外,还为 If-None-Match、If-Modified-Since、Accept 和 Accept-Charset 等最常用的 Header 提供了额外支持。

示例如下:

ResponseSpec responseSpec = headersSpec.header( HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML) .acceptCharset(StandardCharsets.UTF_8) .ifNoneMatch("*") .ifModifiedSince(ZonedDateTime.now()) .retrieve(); 4.7、获取响应

最后一个阶段是发送请求并接收响应。可以使用 exchangeToMono / exchangeToFlux 或 retrieve 方法来实现。

exchangeToMono 和 exchangeToFlux 方法允许访问 ClientResponse 及其 HTTP 状态码和 Header:

Mono response = headersSpec.exchangeToMono(response -> { if (response.statusCode().equals(HttpStatus.OK)) { return response.bodyToMono(String.class); } else if (response.statusCode().is4xxClientError()) { return Mono.just("Error response"); } else { return response.createException() .flatMap(Mono::error); } });

而 retrieve 方法是直接获取响应 Body 的最便捷的方式:

Mono response = headersSpec.retrieve() .bodyToMono(String.class);

需要注意 ResponseSpec.bodyToMono 方法,如果状态码为 4xx(客户端错误)或 5xx(服务器错误),该方法将抛出 WebClientException。

5、使用 WebTestClient

WebTestClient 是测试 WebFlux 服务器端点的主要入口。它的 API 与 WebClient 非常相似,它将大部分工作委托给内部 WebClient 实例,主要侧重于提供测试上下文。DefaultWebTestClient 类是 WebTestClient 的唯一接口实现。

用于测试的客户端可以绑定到真正的服务器上,也可以与特定的 Controller 或 Function 一起使用。

5.1、绑定到服务器

要通过对运行服务器的实际请求完成端到端的集成测试,可以使用 bindToServer 方法:

WebTestClient testClient = WebTestClient .bindToServer() .baseUrl("http://localhost:8080") .build(); 5.2、绑定到路由

可以通过将特定 RouterFunction 传递给 bindToRouterFunction 方法来测试该 RouterFunction:

RouterFunction function = RouterFunctions.route( RequestPredicates.GET("/resource"), request -> ServerResponse.ok().build() ); WebTestClient .bindToRouterFunction(function) .build().get().uri("/resource") .exchange() .expectStatus().isOk() .expectBody().isEmpty(); 5.3、绑定到 WebHandler

使用 bindToWebHandler 方法可以实现相同的行为,该方法需要一个 WebHandler 实例:

WebHandler handler = exchange -> Mono.empty(); WebTestClient.bindToWebHandler(handler).build(); 5.4、绑定到 ApplicationContext

使用 bindToApplicationContext 方法时,会出现更有趣的情况。该方法接收 ApplicationContext,并解析 Context 中的 Controller Bean 和 @EnableWebFlux 配置。

注入一个 ApplicationContext 实例,示例如下:

@Autowired private ApplicationContext context; WebTestClient testClient = WebTestClient.bindToApplicationContext(context) .build(); 5.5、绑定到 Controller

一种更简便的方法是通过 bindToController 方法提供要测试的 Controller 数组。假设已经有了一个 Controller 类,并其注入到了一个需要的类中。如下:

@Autowired private Controller controller; WebTestClient testClient = WebTestClient.bindToController(controller).build(); 5.6、发起请求

构建 WebTestClient 对象后,所有后续操作都与 WebClient 类似,直到 exchange 方法(获取响应的一种方法),该方法提供了 WebTestClient.ResponseSpec 接口,可使用诸如 expectStatus、expectBody 和 expectHeader 等有用的方法进行操作:

WebTestClient .bindToServer() .baseUrl("http://localhost:8080") .build() .post() .uri("/resource") .exchange() .expectStatus().isCreated() .expectHeader().valueEquals("Content-Type", "application/json") .expectBody().jsonPath("field").isEqualTo("value");

Ref:https://www.baeldung.com/spring-5-webclient



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3